package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"reflect"
	"strconv"
	"sync"

	"net/url"

	"strings"

	"encoding/json"

	"bytes"

	"time"

	"github.com/pkg/errors"
	"github.com/robfig/cron"
	"github.com/tidwall/gjson"
)

type apiConfig struct {
	Name       string `yaml:name`
	Array      string `yaml:array`
	Url        string `yaml:url`
	Method     bool   `yaml:method` // TRUE write json in file  |FALSE open api in internet
	Path       string `yaml:path`
	Cronrate   string `yaml:cronrate`
	UrlMethod  string `yaml:urlMethod` //post method needed
	FilterRule string `yaml:filterRule`
	Body       url.Values
	Con        *cron.Cron
}

var waitgroup sync.WaitGroup
var mutex sync.Mutex

func ApiInit() []*apiConfig {
	var acSlice []*apiConfig
	for i, j := range config.ApiList {
		waitgroup.Add(1)
		go acInit(i, j, &acSlice)
	}
	waitgroup.Wait()
	return acSlice
}

func acInit(i string, j map[string]string, acs *[]*apiConfig) {
	var acc apiConfig
	ac := &acc
	value := reflect.ValueOf(ac).Elem()
	t := reflect.TypeOf(ac).Elem()
	for k, v := range j {
		for l := 0; l < t.NumField(); l++ {
			if t.Field(l).Name == k {
				if k == "Method" {
					it, _ := strconv.Atoi(v)
					value.FieldByName(k).Set(reflect.ValueOf(it != 0))
				} else {
					value.FieldByName(k).Set(reflect.ValueOf(v))
				}
			}
		}
	}
	if ac.Name == "" {
		ac.Name = i
	}
	*acs = append((*acs), ac)
	waitgroup.Done()
}

func (ac *apiConfig) HandleInit() {
	if !ac.Method {
		http.HandleFunc("/"+ac.Name, func(w http.ResponseWriter, r *http.Request) {
			r.Header.Add("Content-type", "application/json")
			res, err := ac.getData(ac.Url)
			if err == nil {
				fmt.Fprintln(w, string(res))
			} else {
				logs.Println(err)
			}
		})
	}
}

func (ac *apiConfig) run() {
	if ac.Name == "zwzx_qlqd" {
		ac.Con = cron.New()
		ac.Con.AddFunc(ac.Cronrate, ac.writeZwzx)
		ac.Con.Start()
	} else if ac.Name == "szrx" {
		ac.Con = cron.New()
		ac.Con.AddFunc(ac.Cronrate, ac.writeSzrx)
		ac.Con.Start()
	} else {
		if ac.Method {
			if ac.Path == "" {
				ac.Path = ac.Name + ".json"
			}
			if ac.Cronrate == "" {
				ac.Cronrate = "0 */30 * * * *"
			}
			ac.Con = cron.New()
			ac.Con.AddFunc(ac.Cronrate, ac.writeApiJson)
			ac.Con.Start()
		}
	}
}
func (ac *apiConfig) writeSzrx() {
	var re Requestbody
	var err error
	re.req, err = ac.getData("http://36.101.204.30:25600/workorder12345/webPortal/queryDept?secretKey=505bac2ab2d5a0444345860d52808edc")
	if err == nil {
		result, err := re.Json2maps()
		if err != nil {
			logs.Println(err)
		} else {
			obj := result["obj"].([]interface{})
			for i := 0; i < len(obj); i++ {
				m := obj[i].(map[string]interface{})
				id := m["autoid"].(string)
				path := ac.Path + id + ".json"
				go ac.getSzrx(path, id)
			}
			err = ioutil.WriteFile(ac.Path+"/list.json", re.req, 0666)
			if err != nil {
				logs.Println(err)
			}
		}
	} else {
		logs.Println(err)
	}
}

func (ac *apiConfig) getSzrx(path string, id string) {
	r, err := ac.getData(strings.Replace(ac.Url, "[name]", id, 1))
	if err == nil {
		if r != nil {
			err := ioutil.WriteFile(path, r, 0666)
			if err != nil {
				logs.Println(err)
			} else {
				logs.Println(ac.Name, ac.Url, path, " success")
			}
		} else {
			logs.Println(ac.Name, ac.Url, "can't get data")
		}
	} else {
		logs.Println(err)
	}
}
func (ac *apiConfig) writeZwzx() {

	var re Requestbody
	re.req, _ = ac.getData("http://119.41.208.177:8099/qlqd/json/bm.json")
	result, err := re.Json2map()
	if err != nil {
		logs.Println(err)
	} else {
		for i := 0; i < len(result); i++ {
			id := (result[i]["json"]).(string)
			pathSlice := strings.Split(id, "/")
			path := ac.Path + pathSlice[len(pathSlice)-1]
			r, err := ac.getData(strings.Replace(ac.Url, "[name]", id, 1))
			if err != nil {
				if r != nil {
					err := ioutil.WriteFile(path, r, 0666)
					if err != nil {
						logs.Println(err)
					} else {
						logs.Println(ac.Name, ac.Url, path, " success")
					}
				} else {
					logs.Println(ac.Name, ac.Url, "can't get data")
				}
			}
		}
		err = ioutil.WriteFile(ac.Path+"/list.json", re.req, 0666)
		if err != nil {
			logs.Println(err)
		}
	}
}

func (ac *apiConfig) writeApiJson() {
	var strs []string
	strs = strings.Split(ac.Array, "|")
	if ac.Array == "" {
		strs = strings.Split(ac.Name, "|")
	}
	for i := 0; i < len(strs); i++ {
		url := strings.Replace(ac.Url, "[reqname]", strs[i], 1)
		result, err := ac.getData(urlFormat(url))
		path := strings.Replace(ac.Path, "[reqname]", strs[i], 1)
		if err == nil {
			if result != nil {
				err := ioutil.WriteFile(path, result, 0666)
				if err != nil {
					logs.Println(err)
				} else {
					logs.Println(strs[i], ac.Path, " success")
				}
			} else {
				logs.Println(strs[i], "can't get data")
			}
		} else {
			logs.Println(err)
		}
	}
}
func urlFormat(url string) string {
	temp := timeFormat(url)
	return temp
}

func timeFormat(url string) string {
	for {
		t := time.Now()
		sIndex := strings.Index(url, "[ymd")
		if sIndex == -1 {
			sIndex = strings.Index(url, "[hms")
			if sIndex == -1 {
				break
			}
		}
		i := sIndex + 1
		j := strings.Index(url[i:], "]") + i
		strs := strings.Split(url[i:j], ":")
		for k := 1; k < len(strs); k++ {
			d, _ := time.ParseDuration(strs[k])
			t = t.Add(d)
		}
		if strings.Index(url[i:j], "ymd") > -1 {
			url = strings.Replace(url, `[`+url[i:j]+`]`, t.Format(`2006-01-02`), -1)
			continue
		}
		if strings.Index(url[i:j], "hms") > -1 {
			url = strings.Replace(url, `[`+url[i:j]+`]`, t.Format(`15:04:05`), -1)
			continue
		}
	}
	return url
}

func (ac *apiConfig) getData(url string) (b []byte, err error) {
	var (
		result *http.Response
	)
	if ac.UrlMethod == "POST" {
		param := config.PostParam[ac.Name]
		contentType := param["ContentType"]
		var body []byte
		if contentType == "application/json" {
			delete(param, "ContentType")
			body, err = json.Marshal(param)
		} else {
			temp := ""
			for k, v := range param {
				temp += (k + "=" + v + "&")
			}
			body = []byte(temp)
		}
		result, err = http.Post(url, contentType, bytes.NewBuffer(body))
	} else {
		result, err = http.Get(url)
	}

	if err == nil {
		if result.StatusCode == 200 {
			defer result.Body.Close()
			body, err := ioutil.ReadAll(result.Body)
			if err == nil {
				if ac.FilterRule != "" {
					fres := gjson.Get(string(body), ac.FilterRule)
					fval := fres.Value()
					if fval == nil {
						err = errors.New(ac.Name + " filter rule in operation")
						return nil, err
					}
				}
				return body, nil
			}
		} else {
			err = errors.New("error code:" + string(result.StatusCode))
			return nil, err
		}
	}
	return nil, err
}
